package com.face.server.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.face.RespMsg;
import com.face.server.convert.Event2DormAdminEvent;
import com.face.server.convert.Event2VisitorEvent;
import com.face.server.entity.Admin;
import com.face.server.entity.EventLog;
import com.face.server.repository.EventLogRepository;
import com.face.server.service.UtileService;
import com.face.server.vo.DormAdminEvent;
import com.face.server.vo.VisitorEvent;
import com.mongodb.BasicDBObject;
import com.util.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;


import java.security.Principal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * Created by ryan on 2018/3/20.
 */
@Service
public class EventLogService {
    @Autowired
    private EventLogRepository eventLogRepository;

    @Autowired
    MongoTemplate mongoTemplate;

    public Page<EventLog> listEvent(int type, Pageable pageable, String searchData) {
        if (searchData == null)
            return eventLogRepository.findByTypeIs(type,pageable);
        JSONObject searchObj = JSON.parseObject(searchData);
        boolean isSearchObjEmpty = searchObj.entrySet().stream().allMatch(e -> {
            if (e.getValue() instanceof String) {
                if (StringUtils.equals((String) e.getValue(), ""))
                    return true;
            }
            return false;
        });
        if (isSearchObjEmpty)
            return eventLogRepository.findByTypeIs(type,pageable);

        Date startDate = searchObj.getDate("startDate");
        Date endDate = searchObj.getDate("endDate");
        String key = searchObj.getString("key");

        Criteria dateCri = null;
        if (startDate != null && endDate != null)
            dateCri = Criteria.where("createdDate").gte(startDate).lte(endDate);
        else if (startDate != null && endDate == null)
            dateCri = Criteria.where("createdDate").gte(startDate);
        else if (startDate == null && endDate != null)
            dateCri = Criteria.where("createdDate").lte(endDate);

        Criteria criteria=new Criteria();
        Criteria criteria1=null;
        if (UtileService.isBool(key))
            criteria1=new Criteria("event").is(Boolean.parseBoolean(key));
        criteria.orOperator(new Criteria("name").regex(key),criteria1);

        Criteria integ=new Criteria();
        integ.andOperator(dateCri,criteria);

        int page=pageable.getPageNumber()>0?pageable.getPageNumber()*pageable.getPageSize():0;
        Aggregation aggregation=Aggregation.newAggregation(
                Aggregation.match(integ),
                Aggregation.sort(pageable.getSort()),
                Aggregation.skip((long)page),
                Aggregation.limit(pageable.getPageSize())
        );

        List<EventLog> eventLogs=mongoTemplate.aggregate(aggregation,"eventLog",EventLog.class).getMappedResults();
        return new PageImpl<EventLog>(eventLogs,pageable,eventLogs.size());
    }

    public Page<EventLog> listMyEvent(int type, Pageable pageable, String searchData, Principal principal) {
        Subject subject = SecurityUtils.getSubject();
        Admin admin = (Admin) subject.getPrincipal();
        List<Integer> dormitoryNumList=admin.getDormitoryNumList();
        if (searchData == null)
            return eventLogRepository.findByTypeIsAndBuildNumberIn(type,dormitoryNumList,pageable);
        JSONObject searchObj = JSON.parseObject(searchData);
        boolean isSearchObjEmpty = searchObj.entrySet().stream().allMatch(e -> {
            if (e.getValue() instanceof String) {
                if (StringUtils.equals((String) e.getValue(), ""))
                    return true;
            }
            return false;
        });
        if (isSearchObjEmpty)
            return eventLogRepository.findByTypeIsAndBuildNumberIn(type,dormitoryNumList,pageable);

        Date startDate = searchObj.getDate("startDate");
        Date endDate = searchObj.getDate("endDate");
        String key = searchObj.getString("key");

        Criteria dateCri = null;
        if (startDate != null && endDate != null)
            dateCri = Criteria.where("createdDate").gte(startDate).lte(endDate);
        else if (startDate != null && endDate == null)
            dateCri = Criteria.where("createdDate").gte(startDate);
        else if (startDate == null && endDate != null)
            dateCri = Criteria.where("createdDate").lte(endDate);

        Criteria criteria=new Criteria();
        Criteria criteria1=null;
        if (UtileService.isBool(key))
            criteria1=new Criteria("event").is(Boolean.parseBoolean(key));
        criteria.orOperator(new Criteria("name").regex(key),criteria1);

        Criteria integ=new Criteria();
        integ.andOperator(dateCri,criteria,new Criteria("buildNumber").in(dormitoryNumList));

        int page=pageable.getPageNumber()>0?pageable.getPageNumber()*pageable.getPageSize():0;
        Aggregation aggregation=Aggregation.newAggregation(
                Aggregation.match(integ),
                Aggregation.sort(pageable.getSort()),
                Aggregation.skip((long)page),
                Aggregation.limit(pageable.getPageSize())
        );

        List<EventLog> eventLogs=mongoTemplate.aggregate(aggregation,"eventLog",EventLog.class).getMappedResults();
        return new PageImpl<EventLog>(eventLogs,pageable,eventLogs.size());
    }

    public Object todayFluent(String type, Date begin, Date end, List<Integer> dormitoryNumList, boolean aSuper) throws ParseException {
        Criteria criType=new Criteria();
        criType.and("type").is(Integer.parseInt(type));

        Criteria criBuild=new Criteria();
        if (aSuper==false)
            criBuild=criBuild.and("buildNumber").in(dormitoryNumList);

        Criteria criTime=new Criteria();
        criTime.and("time").gte(begin).lte(end);

        Criteria criteria=new Criteria();
        criteria.andOperator(criTime,criType,criBuild);

        Aggregation aggregation=Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("buildNumber").count().as("total"),
                Aggregation.project("total").and("buildNumber").previousOperation(),
                Aggregation.sort(Sort.Direction.ASC,"buildNumber")
        );
        return mongoTemplate.aggregate(aggregation,"eventLog", BasicDBObject.class).getMappedResults();
    }

    @Autowired
    Event2VisitorEvent event2VisitorEvent;

    @Autowired
    Event2DormAdminEvent event2DormAdminEvent;

    public RespMsg listEvents(Pageable pageable, String begin, String end, String input, Integer type, List<Integer> dormitoryNumList, boolean aSuper) throws ParseException {
        Criteria criTime=new Criteria();
        criTime.and("time").gte(DateUtils.parse(begin)).lte(DateUtils.parse(end));

        Criteria criInput=new Criteria();
        criInput.and("name").regex(input);

        if (input.equals("")==false&&UtileService.isNum(input))
            criInput.and("buildNumber").is(Integer.parseInt(input));

        Criteria criBuild=new Criteria();
        if (aSuper==false)
            criBuild.and("buildNumber").in(dormitoryNumList);

        Criteria criType=new Criteria();
        criType.and("type").is(type);

        List<Criteria> tmp=new ArrayList<>();
        tmp.add(criTime);tmp.add(criInput);tmp.add(criBuild);tmp.add(criType);

        Criteria criteria=new Criteria().andOperator(tmp.stream().toArray(s->new Criteria[s]));

        Aggregation aggregation=Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.skip((long)pageable.getOffset()),
                Aggregation.limit(pageable.getPageSize()),
                Aggregation.sort(pageable.getSort())
        );

        List<EventLog> eventLogs= mongoTemplate.aggregate(aggregation,"eventLog",EventLog.class).getMappedResults();
        if (type==0){
            return RespMsg.build(new PageImpl<EventLog>(eventLogs,pageable,eventLogs.size()));
        }
        if (type==1){
            return RespMsg.build(new PageImpl<VisitorEvent>(event2VisitorEvent.convert(eventLogs),pageable,eventLogs.size()));
        }
        return RespMsg.build(new PageImpl<DormAdminEvent>(event2DormAdminEvent.convert(eventLogs),pageable,eventLogs.size()));
    }
}
